﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Notoric.Model.Elements;

namespace Notoric.ViewModel.Elements
{
    /// <summary>
    /// Adapts a <see cref="MusicSectionElement"/>.
    /// </summary>
    public class MusicSection : Section
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="MusicSection"/> instance.
        /// </summary>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="ImageSectionElement"/>
        /// </para>
        /// <para>
        /// The model image section element adapted by this element.
        /// </para>
        /// </param>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="ViewModelElement"/>
        /// </para>
        /// <para>
        /// The element that directly owns this section.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Any of the arguments is <see langword="null"/>.
        /// </exception>
        public MusicSection(MusicSectionElement adaptedElement, ViewModelElement owner)
            : base(adaptedElement, owner)
        {
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Determines whether the specified composition part is represented in some music section.
        /// </summary>
        /// <param name="composition">
        /// <para>
        /// Type: <see cref="Composition"/>
        /// </para>
        /// <para>
        /// The composition.
        /// </para>
        /// </param>
        /// <param name="firstMeasure">
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The first measure.
        /// </para>
        /// </param>
        /// <param name="lastMeasure">
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The last measure.
        /// </para>
        /// </param>
        /// <returns>
        /// 	<see langword="true"/> if the specified composition has representations; otherwise, <see langword="false"/>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="composition"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// The <paramref name="firstMeasure"/> or <paramref name="lastMeasure"/> is less than 1 or greater than the number of measures in the composition.<br/>
        /// -or-<br/>
        /// The <paramref name="lastMeasure"/> is less than <paramref name="firstMeasure"/>.
        /// </exception>
        public static bool HasRepresentations(Composition composition, ushort firstMeasure, ushort lastMeasure)
        {
            if (composition == null)
                throw new ArgumentNullException("composition");
            if (firstMeasure < 1 || firstMeasure > composition.MeasuresCount)
                throw new ArgumentOutOfRangeException("firstMeasure");
            if (lastMeasure < 1 || lastMeasure > composition.MeasuresCount || lastMeasure < firstMeasure)
                throw new ArgumentOutOfRangeException("lastMeasure");

            var modelComposition = composition.AdaptedElement as Model.Elements.CompositionElement;
            var firstMeasureE = modelComposition[firstMeasure];
            var lastMeasureE = modelComposition[lastMeasure];
            var references = modelComposition.GetReferencingPages(firstMeasureE, lastMeasureE);

            if (references.Count > 0)
                return true;

            return false;
        }

        /// <summary>
        /// Validates a given composition part for a presentation in a music section.
        /// </summary>
        /// <param name="composition">
        /// <para>
        /// Type: <see cref="Composition"/>
        /// </para>
        /// <para>
        /// The presented composition.
        /// </para>
        /// </param>
        /// <param name="presentedParts">
        /// <para>
        /// Type: <see cref="ICollection{T}"/> of <see cref="PartDescriptor"/>
        /// </para>
        /// <para>
        /// The presented parts.
        /// </para>
        /// </param>
        /// <param name="firstMeasure">
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The first presented measure.
        /// </para>
        /// </param>
        /// <param name="lastMeasure">
        /// <para>
        /// Type: <see cref="UInt16"/>
        /// </para>
        /// <para>
        /// The last presented measure.
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="bool"/>
        /// </para>
        /// <para>
        /// <see langword="true"/> if the presented part is valid; <see langword="false"/> otherwise.
        /// </para>
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="composition"/> or <paramref name="presentedParts"/> is <see langword="null"/>
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// The <paramref name="firstMeasure"/> or <paramref name="lastMeasure"/> is less than 1 or greater than the number of measures in <paramref name="composition"/>.<br/>
        /// -or-<br/>
        /// The <paramref name="lastMeasure"/> is less than <paramref name="firstMeasure"/>.
        /// </exception>
        public static bool ValidCompositionPart(Composition composition, ICollection<PartDescriptor> presentedParts, ushort firstMeasure, ushort lastMeasure)
        {
            if (composition == null)
                throw new ArgumentNullException("composition");
            if (presentedParts == null)
                throw new ArgumentNullException("presentedParts");
            if (firstMeasure < 1 || firstMeasure > composition.MeasuresCount)
                throw new ArgumentOutOfRangeException("firstMeasure");
            if (lastMeasure < 1 || lastMeasure > composition.MeasuresCount)
                throw new ArgumentOutOfRangeException("lastMeasure");
            if (lastMeasure < firstMeasure)
                throw new ArgumentOutOfRangeException("lastMeasure");

            var modelComposition = (Model.Elements.CompositionElement)composition.AdaptedElement;
            for (ushort i = firstMeasure; i <= lastMeasure; ++i)
            {
                var measure = modelComposition[i];
                bool allEmpty = true;

                foreach (var part in presentedParts)
                {
                    var pe = measure[part.AdaptedElement as Model.Elements.PartDescriptorElement];
                    if (pe != null && pe.ShortestDuration > 0)
                        allEmpty = false;
                }

                if (allEmpty)
                    return false;
            }

            return true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the presented composition.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Composition"/>
        /// </para>
        /// <para>
        /// The composition presented in this section.
        /// </para>
        /// </value>
        public Composition PresentedComposition
        {
            get
            {
                return FindOwner<Project>().Compositions.First(c => c.AdaptedElement == ((Model.Elements.MusicSectionElement)AdaptedElement).Composition.Composition);
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the dictionary key of a user-friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.MusicSection";

        #endregion
    }
}
